package org.jiuling.wechat.officialaccount.basic;

import com.alibaba.fastjson2.JSON;
import org.jiuling.basics.converter.DateConvert;
import org.jiuling.basics.enums.HttpRequestMethodEnum;
import org.jiuling.basics.validation.BooleanVerify;
import org.jiuling.basics.validation.DateVerify;
import org.jiuling.basics.validation.ObjectVerify;
import org.jiuling.basics.validation.StringVerify;
import org.jiuling.crypto.algorithm.SHA1;
import org.jiuling.crypto.algorithm.UUID;
import org.jiuling.crypto.exception.CryptoException;
import org.jiuling.request.http.HttpRequest;
import org.jiuling.request.http.factory.HttpRequestFactory;
import org.jiuling.request.http.model.HttpRequestBody;
import org.jiuling.request.http.model.HttpRequestParam;
import org.jiuling.wechat.officialaccount.config.WeChatOfficialAccountConfig;
import org.jiuling.wechat.officialaccount.enums.TokenTypeEnum;
import org.jiuling.wechat.officialaccount.exception.WeChatOfficialAccountException;
import org.jiuling.wechat.officialaccount.handle.ResponseHandle;
import org.jiuling.wechat.officialaccount.model.AccessTokenModel;

import java.io.IOException;
import java.util.*;

/**
 * 基础支持
 * @author 紫灵
 * @date 2024年11月24日 23时47分26秒
 */
public class BasicSupport {

    /** 微信公众号配置 */
    private final WeChatOfficialAccountConfig weChatOfficialAccountConfig;
    /** 访问令牌 */
    private static AccessTokenModel accessTokenModel = null;
    /** 稳定版访问令牌强制刷新时间 */
    private Date stableAccessTokenForceRefreshDate = null;
    /** 认证Token */
    private static String authenticationToken;

    public BasicSupport(WeChatOfficialAccountConfig weChatOfficialAccountConfig) {
        this.weChatOfficialAccountConfig = weChatOfficialAccountConfig;
        authenticationToken = weChatOfficialAccountConfig.getAuthenticationToken();
        // 判断Token是否为空，如果为空则设置认证令牌，并重新赋值
        if (StringVerify.isBlank(authenticationToken)){
            authenticationToken = UUID.getUUIDIsWithoutLine();
            weChatOfficialAccountConfig.setAuthenticationToken(authenticationToken);

        }
    }

    /**
     * 获取访问令牌
     * @param tokenType token类型
     * @return 访问令牌模型
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public AccessTokenModel getAccessToken(TokenTypeEnum tokenType) throws WeChatOfficialAccountException {
        return this.getAccessToken(tokenType,null);
    }

    /**
     * 获取访问令牌
     * @param tokenType token类型
     * @param forceRefresh 是否强制刷新
     * @return 访问令牌模型
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public AccessTokenModel getAccessToken(TokenTypeEnum tokenType,Boolean forceRefresh) throws WeChatOfficialAccountException {
        tokenType = ObjectVerify.isNull(tokenType) ? TokenTypeEnum.ORDINARY : tokenType;
        if (Objects.requireNonNull(tokenType) == TokenTypeEnum.STABILITY) {
            return this.getStableAccessToken(forceRefresh);
        }
        return this.getAccessToken();
    }

    /**
     * 获取访问令牌
     * @return 访问令牌模型
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public AccessTokenModel getAccessToken() throws WeChatOfficialAccountException {
        // 判断是否已经获取过访问令牌
        if (!ObjectVerify.isNull(accessTokenModel)){
            if (DateVerify.greaterThanNewDate(accessTokenModel.getExpiresDate())){
                return accessTokenModel;
            }
        }
        // 请求地址
        HttpRequest httpRequest = ApiEnum.GET_ACCESS_TOKEN.getHttpRequest();
        httpRequest.setHttpRequestParam(
                new HttpRequestParam()
                .add("grant_type","client_credential")
                .add("appid",weChatOfficialAccountConfig.getAppId())
                .add("secret",weChatOfficialAccountConfig.getAppSecret())
        );
        try {
            String body = httpRequest.send().getBody();
            // 验证响应信息
            ResponseHandle.handle(body);
            accessTokenModel = JSON.parseObject(body,AccessTokenModel.class);
            return accessTokenModel;
        } catch (IOException e) {
            throw new WeChatOfficialAccountException("获取访问令牌请求异常！");
        }

    }

    /**
     * 获取稳定版访问令牌
     * @param forceRefresh 强制刷新
     * @return 访问令牌模型
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public AccessTokenModel getStableAccessToken(Boolean forceRefresh) throws WeChatOfficialAccountException {

        // 判断是否存在强制刷新时间
        if (!ObjectVerify.isNull(this.stableAccessTokenForceRefreshDate) &&
                // 判断是否为强制刷新
                forceRefresh &&
                // 判断强制刷新时间是否大于30秒，如果30秒内，则不强制刷新
                DateVerify.greaterThanNewDate(DateConvert.modifySecond(this.stableAccessTokenForceRefreshDate,30))){
            forceRefresh = false;
        }

        // 非强制刷新时获取已经获取的令牌
        if (BooleanVerify.isTrue(forceRefresh)){
            this.stableAccessTokenForceRefreshDate = new Date();
        }else {
            // 判断是否已经获取过访问令牌
            if (!ObjectVerify.isNull(accessTokenModel)){
                if (DateVerify.greaterThanNewDate(accessTokenModel.getExpiresDate())){
                    return accessTokenModel;
                }
            }
        }

        // 请求地址
        HttpRequest httpRequest = ApiEnum.GET_STABLE_TOKEN.getHttpRequest();
        httpRequest.setHttpRequestBody(
                new HttpRequestBody()
                        .add("grant_type","client_credential")
                        .add("appid",weChatOfficialAccountConfig.getAppId())
                        .add("secret",weChatOfficialAccountConfig.getAppSecret())
                        .add("force_refresh",forceRefresh)
        );

        try {
            String body = httpRequest.send().getBody();
            // 验证响应信息
            ResponseHandle.handle(body);
            accessTokenModel = JSON.parseObject(body,AccessTokenModel.class);
            return accessTokenModel;
        } catch (IOException e) {
            throw new WeChatOfficialAccountException("获取访问令牌请求异常！");
        }
    }

    /**
     * 获取API接口IP地址
     * @return 接口IP地址列表
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public List<String> getApiDomainIp() throws WeChatOfficialAccountException {
        return this.getApiDomainIp(TokenTypeEnum.STABILITY);
    }

    /**
     * 获取API接口IP地址
     * @param tokenType token类型
     * @return 接口IP地址列表
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public List<String> getApiDomainIp(TokenTypeEnum tokenType) throws WeChatOfficialAccountException {
        return this.getWeChatIp(
                ApiEnum.GET_API_DOMAIN_IP.getHttpRequest(),
                tokenType);
    }

    /**
     * 获取回调IP地址
     * @return 回调IP地址列表
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public List<String> getCallbackIp() throws WeChatOfficialAccountException {
        return this.getCallbackIp(TokenTypeEnum.STABILITY);
    }

    /**
     * 获取回调IP地址
     * @param tokenType 令牌类型
     * @return 回调IP地址列表
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    public List<String> getCallbackIp(TokenTypeEnum tokenType) throws WeChatOfficialAccountException {
        return this.getWeChatIp(
                ApiEnum.GET_CALLBACK_IP.getHttpRequest(),
                tokenType);
    }

    /**
     * 获取微信IP地址
     * @param httpRequest http请求
     * @param tokenType 令牌类型
     * @return 微信IP地址列表
     * @throws WeChatOfficialAccountException 微信公众号异常
     */
    private List<String> getWeChatIp(HttpRequest httpRequest,TokenTypeEnum tokenType) throws WeChatOfficialAccountException {
        AccessTokenModel accessTokenModel = this.getAccessToken(tokenType);
        httpRequest.setHttpRequestParam(
                new HttpRequestParam()
                        .add("access_token",accessTokenModel.getAccessToken())
        );
        try {
            String body = httpRequest.send().getBody();
            // 验证响应信息
            ResponseHandle.handle(body);
            return JSON.parseObject(body)
                    .getJSONArray("ip_list")
                    .parallelStream()
                    .map(String.class::cast)
                    .toList();
        } catch (IOException e) {
            throw new WeChatOfficialAccountException("获取IP地址请求异常！");
        }
    }

    /**
     * API认证
     * @param signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param echostr 随机字符串
     * @return 请求给出的随机字符串
     */
    public String apiAuthentication(String signature,String timestamp,String nonce,String echostr) throws WeChatOfficialAccountException {
        // 封装请求信息
        List<String> words = new ArrayList<>();
        words.add(timestamp);
        words.add(nonce);
        words.add(authenticationToken);
        // 进行排序
        Collections.sort(words);
        try {
            // 生成SHA1加密值，并与signature进行比较
            if (SHA1.decrypt(String.join("", words)).equals(signature)){
                return echostr;
            }else {
                throw new WeChatOfficialAccountException("验证失败！");
            }
        } catch (CryptoException e) {
            throw new WeChatOfficialAccountException(e);
        }
    }

    /**
     * 获取认证Token
     * @return 认证Token
     */
    public String getAuthenticationToken(){
        return authenticationToken;
    }



    private enum ApiEnum{
        /** 获取访问令牌 */
        GET_ACCESS_TOKEN("https://api.weixin.qq.com/cgi-bin/token", HttpRequestMethodEnum.GET),
        /** 获取稳定版访令牌 */
        GET_STABLE_TOKEN("https://api.weixin.qq.com/cgi-bin/stable_token",HttpRequestMethodEnum.POST),
        /** 获取API接口IP地址 */
        GET_API_DOMAIN_IP("https://api.weixin.qq.com/cgi-bin/get_api_domain_ip",HttpRequestMethodEnum.GET),
        /** 获取回调IP地址 */
        GET_CALLBACK_IP("https://api.weixin.qq.com/cgi-bin/getcallbackip",HttpRequestMethodEnum.GET),
        ;

        /** URL地址 */
        private final String url;
        /** 请求方式 */
        private final HttpRequestMethodEnum requestMethod;

        public String getUrl() {
            return url;
        }

        public HttpRequestMethodEnum getRequestMethod() {
            return requestMethod;
        }

        ApiEnum(String url, HttpRequestMethodEnum requestMethod) {
            this.url = url;
            this.requestMethod = requestMethod;
        }

        public HttpRequest getHttpRequest(){
            HttpRequest httpRequest = HttpRequestFactory.openHttpRequest(url);
            httpRequest.setHttpRequestMethod(requestMethod);
            return httpRequest;
        }
    }
}
